// ## 3. 实现一个不可扩展对象，密封，冻结
// 不可扩展，不可以增加新属性，老的属性可以删除，也可以改值
// - 无论是不可扩展，密封，还是冻结，都是浅层控制的，即只控制对象本身属性的增删改。如果对象属性是一个引用类型，比如数组 subArr 或对象 subObj等，虽然subArr、subObj 的不可被删改，但subArr、subObj 的属性仍然可增删改
// - 由于每个对象都有一个属性__proto__,该属性的值是该对象的原型对象，也是引用类型，由于冻结是浅层的所以原型对象并不会被连着冻结，仍然可以通过给对象的原型对象加属性达到给当前对象新增属性的效果。所以如果想进一步冻结还需要把原型对象也冻结上

// ### 3.1 Object.preventExtensions()可以使一个对象不可再添加新的属性，参数为目标对象，返回修改后的对象
var obj = Object.preventExtensions({
    name: 'zhufeng'
});
var obj = { name: 'zhufeng' };
console.log(Object.isExtensible(obj));// true
Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)); // false

//Object.defineProperty(obj, 'age', {value: 10});
//TypeError: Cannot define property age, object is not extensible
obj.age = 10;
console.log(obj.age); // undefined

// ### 3.2 密封
// - Object.seal() 可以使一个对象无法添加新属性的同时，也无法删除旧属性。参数是目标对象，返回修改后的对象
// - 其本质是通过修改属性的 configurable 为 false 来实现的
// - configurable 为 false 时，其他配置不可改变，writable 只能 true 变 false，且属性无法被删除。而由于只要 writable 或 configurable 其中之一为 true，则 value 可改，所以密封之后的对象还是可以改属性值的
// - Object.isSealed() 可以检测一个对象是否密封，即是否可以增删属性。参数是目标对象，返回布尔值，true 代表被密封不可增删属性，false 代表没被密封可增删属性
var obj = new Object();
Object.isExtensible(obj); // true
Object.isSealed(obj); // false
Object.seal(obj);
Object.isExtensible(obj); // false，注意 seal 后对象的 isExtensible() 也随之改变
Object.isSealed(obj); // true

var obj = { name: 'zhufeng' };
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
/**
{
  value: 'zhufeng',
  writable: true,
  enumerable: true,
  configurable: true
}
*/
Object.seal(obj);
console.log(Object.getOwnPropertyDescriptor(obj, 'name')); // seal 后 configurable 变为 false
/**
{
  value: 'zhufeng',
  writable: true,
  enumerable: true,
  configurable: false
}
*/

// ### 3.3 冻结
// - Object.freeze() 可以使对象一个对象不能再添加新属性，也不可以删除旧属性，且不能修改属性的值。参数是目标对象，返回修改后的对象。
// - Object.isFrozen() 可以检测一个对象是否冻结，即是否可以增删改。参数是目标对象，返回布尔值，true 表示已经冻结不可再增删改，false 反之

var obj = new Object();
Object.isExtensible(obj); // true
Object.isSealed(obj); // false
Object.isFrozen(obj); // false
Object.freeze(obj);
Object.isExtensible(obj); // false，注意 freeze 后对象的 isExtensible() 也随之改变
Object.isSealed(obj); // true，注意 freeze 后对象的 isSealed() 也随之改变
Object.isFrozen(obj); // true
var obj = Object.freeze({ name: 'zhufeng' });

// 直接定义新的属性会报错
Object.defineProperty(obj, 'name', {
    value: 'zhufeng'
});

obj.name = 'zhufeng';
obj.name; // undefined

delete obj.name; // 删除失败，返回 false

obj.name = 'jiagou';
obj.name; // 仍然是 "zhufeng"

// ### 总结：
// - #### 不可拓展： preventExtensions()使不可拓展  isExtensible()检测是否可拓展 
//   结果：不可添加 可删除   可修改
// - #### 密封：    seal() 使对象密封               isSealed()   检测是否密封
//   结果：不可添加 不可删除 可修改
// - #### 密封：    freeze() 使对象密封               isFrozen()   检测是否密封
//   结果：不可添加 不可删除 不可修改